/*
	This code is a edited version of Xooxer's Platformer Test Source PixelStep
	Why rewrite something which is already made :P
*/

PixelStep

	proc
		arctan2(x,y)
			if(x == 0 && y == 0) return 0
			return (y>=0)?(arccos(x/sqrt(x**2+y**2))):(-arccos(x/sqrt(x**2+y**2)))

		get_pix_dir(X1, Y1, X2, Y2) // returns the direction from X1,Y1 to X2,Y2
			if(!X1 || !Y1 || !X2 || !Y2) return FALSE // if missing args, retrun
			//if(!(X1-X2)) X1++
			//if(!(Y1-Y2)) Y1++
			var/Dir = arctan2(X1-X2,Y1-Y2)
			return round(Dir)

		deg2dir(deg,min=15,max=165)
			if(!isnum(deg)) return
			if((deg >= -max) && (deg <= -min)) return NORTH
			else if((deg <= max) && (deg >= min)) return SOUTH
			else if((deg <= -max) || (deg >= max)) return EAST
			else if((deg >= -min) || (deg <= min)) return WEST

		get_pix_dist(X1,Y1,X2,Y2)
			if(!isnum(X1) || !isnum(Y1) || !isnum(X2) || !isnum(Y2)) return FALSE
			return max(abs(X1-X2),abs(Y1-Y2))

		// Check for collision between atom A and atom B if
		// atom A were to occupy the Xpos,Ypos pixel coordinates
		get_collision(atom/A, atom/B, Xpos, Ypos)
			// Check for insufficient arguments
			if(!A || !B || !isnum(Xpos) || !isnum(Ypos)) return FALSE

			// No collision if either is non-dense
			if(!A.density || !B.density) return FALSE

			var/Aleft, Aright, Atop, Abottom

			Aleft   = Xpos
			Aright  = Xpos + A.width-1
			Atop    = Ypos + A.height-1
			Abottom = Ypos

			if(Abottom > B.top)    return FALSE
			if(Atop    < B.bottom) return FALSE
			if(Aright  < B.left)   return FALSE
			if(Aleft   > B.right)  return FALSE

			return get_pix_dir(B.midx, B.midy, A.midx, A.midy)

atom
	var
		pixel_step // only effects objects that set pixel_step to 1
		absx // absolute global pixel x location
		absy // absolute global pixel y location

		width	// width of atom in pixels
		height	// height of atom in pixels

		iconx  // x pixel location of the bottom-left corner of the atom in its icon
		icony  // y pixel location of the bottom-left corner of the atom in its icon

		left
		right

		top
		bottom

		midx
		midy

		friction

	New()
		if(pixel_step)
			// We'll be animating our own movement, so set this to 0
			var/list/L = loc2pix()
			if(L&&L.len)
				var/X = L["1"], Y = L["2"]
				absx = X; absy = Y

			left   = absx
			right  = absx + width-1
			top    = absy + height-1
			bottom = absy
			midx   = absx + round(width>>1)
			midy   = absy + round(height>>1)

		..() // Initialize and update the pixel position for new atoms.

	proc
		// convert a tile location and pixel offset to pixel coordinates
		loc2pix(mode) // mode 1 returns text, mode 0 for list
			if(!loc) return FALSE // does not work with areas
			var/ABSX = ((((x-1)<<5)+iconx+pixel_x) || ICON_MIN)
			var/ABSY = ((((y-1)<<5)+icony+pixel_y) || ICON_MIN)
			if(mode) return "[ABSX], [ABSY]"
			return list("1"=ABSX, "2"=ABSY)

		// convert pixel coordinates to a tile location and pixel offset
		pix2loc(PixX,PixY, mode) // mode 1 returns text, mode 0 for list
			if(!PixX || !PixY) return FALSE // does not work with areas
			var/X, Y, pixx, pixy
			X = round(PixX>>5)+1
			Y = round(PixY>>5)+1
			pixx = (PixX%ICON_WIDTH)-iconx
			pixy = (PixY%ICON_WIDTH)-icony
			if(mode) return "[X]:[pixx], [Y]:[pixy]"
			return list("1"=X,"2"=pixx,"3"=Y,"4"=pixy)

		PixBump(atom/movable/A, BumpDir) // Called when an atom is bumped
			return (BumpDir || TRUE) // default returns the direction of the Bump or TRUE

	movable

		var
			movement_state // The icon_state to use when moving
			movement_delay // how many ticks the animation lasts
			moving // Boolean toggle. 1 moving, 0 not moving
			init // tracks inital placement
			atom/base // object currently supporting the atom, keeping it from falling

		New()
			..() // Initialize and update the pixel position for new atoms.
			if(pixel_step)
				// We'll be animating our own movement, so set this to 0
				animate_movement = 0
				UpdateLocPix(absx, absy, z, TRUE)

		proc
			UpdatePixLoc() // recalculates the absolute pixel position
				// get mode 0 pixel position from loc
				var/list/L = loc2pix()
				// set absolute variables if loc exists
				if(L) {absx=L["1"]; absy=L["2"]}

			// updates the tile location and pixel offset
			UpdateLocPix(X, Y, Zpos, Init)
				if(!X||!Y) return FALSE
				// get mode 0 location from abs pixels
				var/list/L = pix2loc(X,Y)
				if(L && L.len)
					var/turf/T = locate(L["1"], L["3"],(Zpos||z))
					if(!T) return FALSE
					if(loc != T)
						if(!Move(T,0,!Init))
							if(Init) pixel_x=L["2"]; pixel_y=L["4"]
							return FALSE
					pixel_x=L["2"]; pixel_y=L["4"]
					left   = absx
					right  = absx + width-1
					top    = absy + height-1
					bottom = absy
					midx   = absx + round(width>>1)
					midy   = absy + round(height>>1)
					if(!istype(src,/mob/player))
						if(movement_state && !moving)
							moving = 1
							flick(movement_state, src)
							spawn(movement_delay*world.tick_lag) moving = 0
					/*
					if(istype(src,/mob/player))
						var/mob/player/M = src
						if(!M.jumping && !M.falling && abs(M.hspeed)>2)
							if(movement_state && !moving)
								moving = 1
								flick(movement_state, src)
								spawn(movement_delay*world.tick_lag) moving = 0
					else
						if(movement_state && !moving)
							moving = 1
							flick(movement_state, src)
							spawn(movement_delay*world.tick_lag) moving = 0
					*/

					if(!loc) return FALSE
					return TRUE

			PixMove(Xpos, Ypos, Zpos, NewDir)
				dir = (NewDir || dir) // establish direction
				if(!Xpos||!Ypos) return // Insufficient arguments

				// translate our loc to pixel coordinates
				var/list/PixLoc = loc2pix()
				if(!PixLoc || !PixLoc.len) return

				for(var/mob/M in range(1,src)-src)
					if(!M.pixel_step) continue
					var/BumpDir = Choreographer.get_collision(src, M, Xpos, Ypos)
					if(BumpDir) if(M.PixBump(src, BumpDir)) return 0

				for(var/atom/A in range(1,src)-src)
					if(!A.pixel_step) continue
					var/BumpDir = Choreographer.get_collision(src, A, Xpos, Ypos)
					if(BumpDir) if(A.PixBump(src, BumpDir)) return 0
				. = UpdateLocPix(Xpos, Ypos, Zpos) // try to update our location
				// update our absolute pixel variables if a movement was made
				if(.){absx = Xpos; absy = Ypos}

			get_pix_step(Dir, StepSize)
				if(!isnum(Dir)) return
				StepSize = (StepSize || pixel_step_size)
				if(!StepSize) return
				var/X = absx, Y = absy
				if(Dir&NORTH)Y += StepSize
				else if(Dir&SOUTH)Y -= StepSize
				if(Dir&EAST)X += StepSize
				else if(Dir&WEST)X -= StepSize
				return list("1" = X, "2" = Y)

turf
	Enter(atom/movable/A)
		.=..() // set our return value to the default by calling the parent
		if(A.pixel_step) // if this is one of our pixelstep atoms
			if(!A.init) // if the atom has not been initialized yet
				A.init = TRUE // do so now by setting its init value to 1
				return . // and return normally so the item may be placed
			else if(!.) // Otherwise, if it can't normally enter the turf
				return !density // allow it to enter the turf if it is not dense